﻿using System;
using System.Collections.Generic;
using System.Linq;
using RayDen.Library.Core;
using RayDen.Library.Core.Primitives;
using RayDen.Library.Data.Imaging;
using RayDen.RayEngine.Core.Interface;
using RayDen.RayEngine.Core.Types;
using RayDen.RayEngine.Engines.PathTracer;
using RayDen.RayEngine.Engines.SpectralPT;
using RayDen.RayEngine.Scene;

namespace RayDen.RayEngine.Engines.XP
{

    public class ScatteringPoint
    {
        public Point Point;
        public Normal GeoNormal, ShadingNormal;
        public Vector IncomingDirection;
        public float IncomingDistance;
        public IntersectionType HitType;
    }

    public class HeroWavelengthPathSampler : PathSamplerBase
    {
        internal SampledSpectrum Throughput;
        public PathTracerPathState PathState;

        protected internal SpectralSamplingHelper wlSampler;


        protected int depth, tracedShadowRayCount;
        protected float pathWeight;
        protected bool specularBounce;

        public UInt64 PathExpression;
        public new SampledSpectrum Radiance;

        protected RayEngineScene scene;
        private SpectralShadowRayInfo[] secRays;
        SurfaceIntersectionData hitInfo = null;

        protected float HeroWavelength, HwPdf;


        public int MaxRaysPerPath
        {
            get;
            set;
        }


        public override void InitPath(IPathProcessor buffer)
        {
            base.InitPath(buffer);
            this.scene = pathIntegrator.Scene;
            this.Radiance = new SampledSpectrum(0f);
            this.Throughput = new SampledSpectrum(1f);
            this.PathState = PathTracerPathState.EyeVertex;
            if (this.secRays == null)
                this.secRays = new SpectralShadowRayInfo[scene.ShadowRaysPerSample];
            this.Sample = pathIntegrator.Sampler.GetSample(null);
            pathIntegrator.Scene.Camera.GetRay(Sample.imageX, Sample.imageY, out this.PathRay);
            this.RayIndex = -1;
            this.pathWeight = 1.0f;
            this.tracedShadowRayCount = 0;
            this.depth = 0;
            this.specularBounce = true;
        }


        public override void Splat(SampleBuffer sampleBuffer)
        {

            base.Radiance = ColorFactory.ToRgb(ref this.Radiance);//this.Radiance.ToRgb();
            base.Splat(sampleBuffer);
        }

        public override bool FillRayBuffer(RayBuffer rayBuffer)
        {
            var leftSpace = rayBuffer.LeftSpace();
            if (((PathState == PathTracerPathState.EyeVertex) && (1 > leftSpace)) ||
            ((PathState == PathTracerPathState.ShadowRaysOnly) && (tracedShadowRayCount > leftSpace)) ||
            ((PathState == PathTracerPathState.NextVertex) && (tracedShadowRayCount + 1 > leftSpace)))
                return false;
            if (PathState != PathTracerPathState.ShadowRaysOnly)
                RayIndex = rayBuffer.AddRay(ref PathRay);
            if (PathState == PathTracerPathState.NextVertex || PathState == PathTracerPathState.ShadowRaysOnly)
            {
                for (int i = 0; i < tracedShadowRayCount; ++i)
                    secRays[i].currentShadowRayIndex = rayBuffer.AddRay(ref secRays[i].shadowRay);
            }
            return true;
        }

        public override void Advance(RayBuffer rayBuffer, SampleBuffer consumer)
        {
            base.Advance(rayBuffer, consumer);
            //Propagation

            var rayHit = rayBuffer.rayHits[RayIndex];
            depth++;
            var point = new ScatteringPoint();
            if (rayHit.Miss() || depth > scene.MaxPathDepth)
            {
                point.HitType = IntersectionType.Environment;
                point.IncomingDirection = -PathRay.Dir;
                this.SplatScatteringPoint(point, consumer);
                return;
            }


        }

        private void SplatScatteringPoint(ScatteringPoint point, SampleBuffer sb)
        {
            switch (point.HitType)
            {
                case IntersectionType.Environment:
                    SampledSpectrum env;
                    this.SampleEnvironment(ref point.IncomingDirection, out env);
                    this.Radiance += Throughput * env;
                    break;
            }

        }

        private void SampleEnvironment(ref Vector vector, out SampledSpectrum result)
        {
            result = ColorFactory.FromRgb(this.LightSampler.SampleEnvironment(-vector), SpectrumType.Illuminant);
        }
    }
}
